package cc.rengu.igas.channel.wechat.core.realize.impl;

import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.common.enums.AlgorithmTypeEnum;
import cc.rengu.igas.channel.wechat.core.model.bean.WechatPaySign;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.oltp.service.common.entity.CertInfo;
import cc.rengu.oltp.service.realize.HsmService;
import cc.rengu.oltp.service.realize.impl.HsmServiceImpl;
import cc.rengu.oltp.utility.util.MD5Util;
import cc.rengu.oltp.utility.util.RSAUtil;
import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import org.apache.commons.codec.binary.Base64;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 签名服务实现
 */
public class WechatSignServiceImpl implements WechatSignService {
    private static final String algorRSA2 = "SHA256WithRSA";
    private static final String charset = "UTF-8";
    private static String KEY_TYPE = "&key=";
    private RgLogger rgLog = RgLog.getLogger(this.getClass().getName());

    @Override
    public String generateSignString(String signData, CertInfo certInfo) throws Exception {
        RSAPrivateKey privateKey = null;
        if ("0".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            if (certInfo.getAlgorithmType() != null && certInfo.getAlgorithmType().contains(AlgorithmTypeEnum.RSA.getTypeCode())) {
                privateKey = RSAUtil.getPrivateKeyFromBase64String(certInfo.getCertValue());
                return RSAUtil.generateSignString(signData, privateKey, (null == certInfo.getAlgorithmType()) ? algorRSA2 : certInfo.getAlgorithmType(), charset);
            } else {
                rgLog.debug("不支持加密算法类型AlgorithmType:<{}>,", certInfo.getAlgorithmType());
                return null;
            }

        } else if ("1".equals(certInfo.getCertSaveType())) {
            //调用加密机需自己客户化
            String algorithmType = "SM3";//加密机默认国密摘要
            rgLog.debug("使用加密机加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            if (certInfo.getAlgorithmType() != null) {
                if (certInfo.getAlgorithmType().contains("SHA256")) {
                    algorithmType = "SHA256";
                } else if (certInfo.getAlgorithmType().contains("RSA")) {
                    algorithmType = "SHA1";
                }
            }
            HsmService hsmService = new HsmServiceImpl();
            return hsmService.hsmGenerateSignString(null, certInfo.getCertId(), signData, algorithmType);
        } else if ("2".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            /*优先从系统缓存读取*/
            String priKeyStr = (String) UnifiedCache.get(WechatParamConstant.CERT_KEY_CACHE, certInfo.getCertIndex());
            if (priKeyStr != null) {
                privateKey = RSAUtil.getPrivateKeyFromBase64String(priKeyStr);
            } else {
                /*从本地证书文件获取*/
                List<String> aliasList = RSAUtil.getCertAliasFromPfxCertFile(certInfo.getCertSavePath(), certInfo.getCertPassword());
                privateKey = RSAUtil.getPrivateKeyFromCertFile(certInfo.getCertSavePath(), aliasList.get(0), certInfo.getCertPassword());
                if (privateKey != null) {
                    if (UnifiedCache.set(WechatParamConstant.CERT_KEY_CACHE, certInfo.getCertIndex(), Base64.encodeBase64String(privateKey.getEncoded())) != 0) {
                        rgLog.error("设置证书缓存索引号{}缓存失败", certInfo.getCertIndex());
                    }
                }
            }
            if (privateKey != null) {
                return RSAUtil.generateSignString(signData, privateKey, certInfo.getAlgorithmType(), charset);
            } else {
                return null;
            }

        } else {
            rgLog.debug("不支持密钥存储类型CertSaveType:<{}>,", certInfo.getCertSaveType());
            return null;
        }
    }

    @Override
    public boolean verifySignString(String sign, String signBlock, CertInfo certInfo) throws Exception {
        RSAPublicKey publicKey = null;
        // 判断证书/密钥类型,通过软加密还是硬加密方式（0-本地数据库存储,1-加密机,2-本地文件）
        if ("0".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            publicKey = RSAUtil.getPublicKeyFromBase64String(certInfo.getCertValue());
            return RSAUtil.verifySignString(signBlock, sign, publicKey, (null == certInfo.getAlgorithmType()) ? algorRSA2 : certInfo.getAlgorithmType(), charset);
        } else if ("1".equals(certInfo.getCertSaveType())) {
            //需要自己客户化
            String algorithmType = "SM3";//加密机默认国密摘要
            rgLog.debug("使用加密机加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            if (certInfo.getAlgorithmType() != null) {
                if (certInfo.getAlgorithmType().contains("SHA256")) {
                    algorithmType = "SHA256";
                } else if (certInfo.getAlgorithmType().contains("RSA")) {
                    algorithmType = "SHA1";
                }
            }
            HsmService hsmService = new HsmServiceImpl();
            return hsmService.hsmVerifySignString(null, certInfo.getCertId(), signBlock, algorithmType, sign);
        } else if ("2".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            /*优先从系统缓存读取*/
            String pubKeyStr = (String) UnifiedCache.get(WechatParamConstant.CERT_KEY_CACHE, certInfo.getCertIndex());
            if (pubKeyStr != null) {
                publicKey = RSAUtil.getPublicKeyFromBase64String(pubKeyStr);
            } else {
                /*从本地证书文件获取*/
                publicKey = RSAUtil.getPublicKeyFromCertFile(certInfo.getCertSavePath());
                if (publicKey != null) {
                    if (UnifiedCache.set(WechatParamConstant.CERT_KEY_CACHE, certInfo.getCertIndex(), Base64.encodeBase64String(publicKey.getEncoded())) != 0) {
                        rgLog.error("设置证书缓存索引号{}缓存失败", certInfo.getCertIndex());
                    }
                }
            }
            if (publicKey != null) {
                return RSAUtil.verifySignString(signBlock, sign, publicKey, certInfo.getAlgorithmType(), charset);
            } else {
                return false;
            }
        }
        return false;
    }

    @Override
    public String generateMD5SignString(String signBlock, String signKey) {
        return MD5Util.generateSignString(signBlock, KEY_TYPE, signKey);
    }

    @Override
    public boolean verifyMD5SignString(String signData, String signBlock, String signKey) {
        return MD5Util.verifySignString(signData, signBlock, KEY_TYPE, signKey);
    }

    @Override
    public String generateSignBlockString(String xmlString) {
        try {
            if (null != xmlString && 0 != xmlString.trim().length()) {
                ArrayList<String> arrayList = new ArrayList<>();
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = factory.newDocumentBuilder();
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlString.getBytes());
                Document document = documentBuilder.parse(byteArrayInputStream);
                NodeList allNodes = document.getFirstChild().getChildNodes();
                Node node;
                int i = 0;
                while (i < allNodes.getLength()) {
                    node = allNodes.item(i);
                    if (node instanceof Element) {
                        if (!WechatTreeNodeConstant.WX_SIGN.equals(node.getNodeName()) && null != node.getTextContent() && 0 != node.getTextContent().length()) {
                            arrayList.add(node.getNodeName() + "=" + node.getTextContent() + "&");
                        }
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[arrayList.size()]);
                Arrays.sort(strArray);
                StringBuilder stringBuilder = new StringBuilder();
                for (i = 0; i < strArray.length; i++) {
                    stringBuilder.append(strArray[i]);
                }
                return stringBuilder.substring(0, stringBuilder.length() - 1);
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    @Override
    public String generatePaySignString(WechatPaySign wechatPaySign) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(WechatTreeNodeConstant.WX_APP_ID + "=" + wechatPaySign.getAppId() + "&");
        arrayList.add(WechatTreeNodeConstant.WX_RAND_STR + "=" + wechatPaySign.getNonceStr() + "&");
        arrayList.add(WechatTreeNodeConstant.WX_PACKAGE + "=" + wechatPaySign.getPayPackage() + "&");
        arrayList.add(WechatTreeNodeConstant.WX_SIGN_TYPE + "=" + wechatPaySign.getSignType() + "&");
        arrayList.add(WechatTreeNodeConstant.WX_TIME_STAMP + "=" + wechatPaySign.getTimeStamp() + "&");
        String[] strArray = arrayList.toArray(new String[arrayList.size()]);
        Arrays.sort(strArray);
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < strArray.length; i++) {
            stringBuilder.append(strArray[i]);
        }
        String paySignBlock = stringBuilder.substring(0) + WechatTreeNodeConstant.WX_SIGN_KEY + "=" + wechatPaySign.getSignKey();
        rgLog.info("支付签名串:<{}>", paySignBlock);
        return MD5Util.generateSignString(paySignBlock);
    }

    @Override
    public String rsaEncSensitiveInfo(String sensitiveInfo, CertInfo certInfo, String padding) throws Exception {
        // 判断证书/密钥类型,通过软加密还是硬加密方式（0-本地数据库存储,1-加密机,2-本地文件）
        if ("0".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            RSAPublicKey publicKey = RSAUtil.getPublicKeyFromBase64String(certInfo.getCertValue());
            if (padding == null) {
                return RSAUtil.publicKeyEncrypt(sensitiveInfo, publicKey);
            } else {
                return Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt(publicKey, sensitiveInfo.getBytes(), padding));
            }
        } else if ("1".equals(certInfo.getCertSaveType())) {
            //需要自己客户化
            return null;
        } else if ("2".equals(certInfo.getCertSaveType())) {
            rgLog.debug("使用软加密,算法类型:<{}>,", certInfo.getAlgorithmType());
            RSAPublicKey publicKey = RSAUtil.getPublicKeyFromCertFile(certInfo.getCertSavePath());
            if (padding == null) {
                return RSAUtil.publicKeyEncrypt(sensitiveInfo, publicKey);
            } else {
                return Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt(publicKey, sensitiveInfo.getBytes(), padding));
            }
        } else {
            rgLog.debug("不支持密钥存储类型:<{}>,", certInfo.getCertSaveType());
            return null;
        }
    }
}
